const express = require("express");
const fileUpload = require("express-fileupload");
const { Sequelize } = require("sequelize");
const fs = require("fs");
const path = require("path");
const { Product, Project, Jmx, Execution: ExecutionDb } = require("../db");
const { JmxWorker } = require("../jmxGeneration")
const { Execution, Registration } = require("../remoteMachine")
const { FileUtil } = require("../util");
const config = require("../../config");

const router = express.Router();
router.use(fileUpload());

router.post("/generateJmxs", async (req, res) => {
    try {
        const { uuid } = req.body;
        let hars = await Jmx.findAll({
            where: { uuid: uuid },
            attributes: [
                "id",
                ["test_plan_name", "testPlanName"],
            ]
        });
        hars = hars.map(har => har.toJSON());
        for (const har of hars) {
            const harPath = path.resolve(config.path.data, "har", uuid);
            const jmxPath = path.resolve(config.path.data, "jmx", uuid);
            const harFilterConfigs = [];
            const testPlanName = har.testPlanName;
            const inputConfigs = [];
            const outputConfigs = [];
            const headerConfig = null;
            const forcedRequestConfigs = [];
            const forcedResponseConfigs = [];
            const mapRequestConfigs = [];
            const mapResponseConfigs = [];
            const customRequestConfigs = [];
            const ignoreRequestConfigs = [];
            const ignoreResponseConfigs = [];
            const stepConfig = {};
            const jmxWorker = new JmxWorker({ "host": "127.0.0.1", "port": 6379, "db": 0 });
            jmxWorker.work(harPath,
                jmxPath,
                harFilterConfigs,
                testPlanName,
                inputConfigs,
                outputConfigs,
                headerConfig,
                forcedRequestConfigs,
                forcedResponseConfigs,
                mapRequestConfigs,
                mapResponseConfigs,
                customRequestConfigs,
                ignoreRequestConfigs,
                ignoreResponseConfigs,
                stepConfig
            );
            const jmxFileNames = (await FileUtil.getFileNames(jmxPath)).filter(fileName => fileName.startsWith(testPlanName));
            const jmxFilePaths = jmxFileNames.map(fileName => path.join("data", "jmx", uuid, fileName));
            await Jmx.update({ jmx_file_paths: jmxFilePaths.toString() }, { where: { id: har.id } });
        }
        let jmxs = await Jmx.findAll({
            where: { uuid: uuid },
            attributes: [
                "uuid",
                ["test_plan_name", "testPlanName"],
                ["jmx_file_paths", "jmxFilePaths"],
                ["created_at", "createdAt"],
                ["updated_at", "updatedAt"]
            ],
            include: [
                {
                    model: Product,
                    attributes: [["product_name", "productName"]],
                    required: false,
                },
                {
                    model: Project,
                    attributes: [["project_name", "projectName"]],
                    required: false,
                }
            ]
        });
        jmxs = jmxs.map(jmx => jmx.toJSON());
        jmxs = jmxs.reduce((acc, cur) => {
            if (Object.keys(acc).length === 0) {
                acc = {
                    uuid: cur.uuid,
                    productName: cur.Product && cur.Product.productName,
                    projectName: cur.Project && cur.Project.projectName,
                    testPlanList: [{
                        testPlanName: cur.testPlanName,
                        jmxFilePath: cur.jmxFilePath,
                        createdAt: cur.createdAt,
                        updatedAt: cur.updatedAt
                    }]
                }
            }
            else {
                acc.testPlanList.push({
                    testPlanName: cur.testPlanName,
                    jmxFilePath: cur.jmxFilePath,
                    createdAt: cur.createdAt,
                    updatedAt: cur.updatedAt
                })
            }
            return acc;
        }, {})
        const response = jmxs;
        res.customResponse(200, "success", response);
    } catch (error) {
        console.log(error);
        res.customResponse(500, error.message, {});
    }
});

router.post("/getJmxList", async (req, res) => {
    try {
        const uuid = req.body.uuid;
        const productId = req.body.productId || null;
        const projectId = req.body.projectId || null;
        let products = await Product.findAll({
            attributes: [
                ["id", "productId"],
                ["product_name", "productName"]
            ]
        });
        products = products.map(product => product.toJSON());
        let projects = await Project.findAll({
            attributes: [
                ["id", "projectId"],
                ["project_name", "projectName"]
            ]
        });
        projects = projects.map(project => project.toJSON());
        let jmxs = await Jmx.findAll({
            where: {
                ...(uuid ? { uuid: uuid } : {}),
                ...(productId ? { product_id: productId } : {}),
                ...(projectId ? { project_id: projectId } : {}),
                "jmx_file_paths": {
                    [Sequelize.Op.and]: [
                        { [Sequelize.Op.not]: null },
                        { [Sequelize.Op.ne]: "" }
                    ]
                }
            },
            attributes: [
                "id",
                "uuid",
                ["product_id", "productId"],
                ["project_id", "projectId"],
                ["test_plan_name", "testPlanName"],
                ["jmx_file_paths", "jmxFilePaths"],
                ["created_at", "createdAt"],
                ["updated_at", "updatedAt"]
            ]
        });
        jmxs = jmxs.map(jmx => jmx.toJSON());
        jmxs = jmxs.reduce((acc, cur) => {
            if (!acc[cur.uuid]) {
                acc[cur.uuid] = {
                    uuid: cur.uuid,
                    productName: products.find(product => product.productId === cur.productId)?.productName || null,
                    projectName: projects.find(project => project.projectId === cur.projectId)?.projectName || null,
                    jmxList: [{
                        testPlanName: cur.testPlanName,
                        jmxFilePaths: cur.jmxFilePaths.split(","),
                        createdAt: cur.createdAt,
                        updatedAt: cur.updatedAt
                    }],
                    createdAt: jmxs.filter(har => har.uuid === cur.uuid).reduce((acc, cur) => {
                        return cur.createdAt < acc ? cur.createdAt : acc
                    }, cur.createdAt),
                    updatedAt: jmxs.filter(har => har.uuid === cur.uuid).reduce((acc, cur) => {
                        return cur.updatedAt > acc ? cur.updatedAt : acc
                    }, cur.updatedAt)
                };
            } else {
                acc[cur.uuid].jmxList.push({
                    testPlanName: cur.testPlanName,
                    jmxFilePaths: cur.jmxFilePaths.split(","),
                    createdAt: cur.createdAt,
                    updatedAt: cur.updatedAt
                });
            }
            return acc;
        }, {});
        const response = Object.keys(jmxs).map(key => jmxs[key]).sort((jmx1, jmx2) => {
            if (jmx1.updatedAt === jmx2.updatedAt) {
                return jmx2.createdAt - jmx1.createdAt;
            }
            return jmx2.updatedAt - jmx1.updatedAt;
        });
        res.customResponse(200, "success", response);
    } catch (error) {
        console.log(error);
        res.customResponse(500, error.message, {});
    }
});

router.post("/winex/generateJmxs", async (req, res) => {
    try {
        const uuid = req.body.uuid;
        let hars = await Jmx.findAll({
            where: { uuid: uuid },
            attributes: [
                "id",
                ["test_plan_name", "testPlanName"],
            ]
        });
        hars = hars.map(har => har.toJSON());
        const testPlanNames = Object.keys(config.jmx.winex).map(key => config.jmx.winex[key].testPlanName);
        for (const har of hars) {
            if (!testPlanNames.includes(har.testPlanName)) {
                return res.customResponse(400, `HAR文件名必须是[${testPlanNames.toString()}]`, {});
            }
        }
        for (const har of hars) {
            const testPlanName = har.testPlanName;
            const businessName = Object.keys(config.jmx.winex).find(businessName => config.jmx.winex[businessName].testPlanName === testPlanName);
            const harPath = path.resolve(config.path.data, "har", uuid);
            const harFilterConfigs = config.harFilterConfig.winex[businessName];
            const jmxPath = path.resolve(config.path.data, "jmx", uuid);
            const inputConfigs = config.jmx.winex[businessName].inputConfigs;
            const outputConfigs = config.jmx.winex[businessName].outputConfigs;
            const headerConfig = config.jmx.winex[businessName].headerConfig;
            const forcedRequestConfigs = config.jmx.winex[businessName].forcedRequestConfigs;
            const forcedResponseConfigs = config.jmx.winex[businessName].forcedResponseConfigs;
            const mapRequestConfigs = config.jmx.winex[businessName].mapRequestConfigs;
            const mapResponseConfigs = config.jmx.winex[businessName].mapResponseConfigs;
            const customRequestConfigs = config.jmx.winex[businessName].customRequestConfigs;
            const ignoreRequestConfigs = config.jmx.winex[businessName].ignoreRequestConfigs;
            const ignoreResponseConfigs = config.jmx.winex[businessName].ignoreResponseConfigs;
            const stepConfigs = config.stepConfig.winex[businessName];
            const jmxWorker = new JmxWorker({ "host": "127.0.0.1", "port": 6379, "db": 0 });
            jmxWorker.work(harPath,
                jmxPath,
                harFilterConfigs,
                testPlanName,
                inputConfigs,
                outputConfigs,
                headerConfig,
                forcedRequestConfigs,
                forcedResponseConfigs,
                mapRequestConfigs,
                mapResponseConfigs,
                customRequestConfigs,
                ignoreRequestConfigs,
                ignoreResponseConfigs,
                stepConfigs
            );
            const jmxFileNames = (await FileUtil.getFileNames(jmxPath)).filter(fileName => fileName.startsWith(testPlanName));
            const jmxFilePaths = jmxFileNames.map(fileName => path.join("data", "jmx", uuid, fileName));
            await Jmx.update({ jmx_file_paths: jmxFilePaths.toString() }, { where: { id: har.id } });
        }
        let jmxs = await Jmx.findAll({
            where: {
                uuid: uuid
            },
            attributes: [
                "uuid",
                ["product_id", "productId"],
                ["project_id", "projectId"],
                ["test_plan_name", "testPlanName"],
                ["jmx_file_paths", "jmxFilePaths"],
                ["created_at", "createdAt"],
                ["updated_at", "updatedAt"]
            ],
            include: [
                {
                    model: Product,
                    attributes: [["product_name", "productName"]],
                    required: false,
                },
                {
                    model: Project,
                    attributes: [["project_name", "projectName"]],
                    required: false,
                }
            ]
        });
        jmxs = jmxs.map(jmx => jmx.toJSON());
        jmxs = jmxs.reduce((acc, cur) => {
            if (Object.keys(acc).length === 0) {
                acc = {
                    uuid: cur.uuid,
                    productName: cur.Product && cur.Product.productName,
                    projectName: cur.Project && cur.Project.projectName,
                    testPlanList: [{
                        testPlanName: cur.testPlanName,
                        jmxFilePath: cur.jmxFilePath,
                        createdAt: cur.createdAt,
                        updatedAt: cur.updatedAt
                    }]
                }
            }
            else {
                acc.testPlanList.push({
                    testPlanName: cur.testPlanName,
                    jmxFilePath: cur.jmxFilePath,
                    createdAt: cur.createdAt,
                    updatedAt: cur.updatedAt
                })
            }
            return acc;
        }, {})
        const response = jmxs;
        res.customResponse(200, "success", response);
    } catch (error) {
        console.log(error);
        res.customResponse(500, error.message, {});
    }
});

router.post("/uploadJmxFiles", async (req, res) => {
    try {
        if (!(req.files && req.files.jmxFiles)) {
            return res.customResponse(400, "未上传Jmx文件", {});
        }
        const uuid = req.body.uuid || null;
        const jmxFiles = Array.isArray(req.files.jmxFiles) ? req.files.jmxFiles : [req.files.jmxFiles];
        const jmxPath = path.resolve(config.path.data, "jmx", uuid);
        let isUpdated = false;
        for (const jmxFile of jmxFiles) {
            const jmxFileName = decodeURIComponent(jmxFile.name);
            const jmxFilePath = path.resolve(jmxPath, jmxFileName);
            const testPlanName = jmxFileName.replace(/\.[^.]+$/, "").split("_")[0];
            if (fs.existsSync(jmxFilePath)) {
                await jmxFile.mv(jmxFilePath);
                await Jmx.increment("jmx_version", { by: 1, where: { uuid: uuid, test_plan_name: testPlanName } });
                isUpdated = true;
            }
        }
        if (!isUpdated) {
            return res.customResponse(400, "JMX文件上传失败", {});
        }
        const response = {
            uuid: uuid
        };
        res.customResponse(200, "success", response);
    } catch (error) {
        console.log(error);
        res.customResponse(500, error.message, {});
    }
});

module.exports = router;
